// scaup - Scala up!
// (c) 2008-2010, Normen Müller, normen.mueller@gmail.com  
// $Id: E.scala 53 2010-01-16 13:09:50Z normen.mueller $
package scaup.draft.eq

import scaup.Views._

//trait Identifiable[T] {
//  val id: T
//}
//
//case class Annotation[K,V](val key: K, val value: V) extends Equiv[Annotation[K,V]] { self: Annotation[K,V] =>
//  def equiv(x: Annotation[K,V]): Boolean = equiv(this, x)
//  def equiv(x: Annotation[K,V], y: Annotation[K,V]) = x == y
//}
//
//case class Constituent[T](val value: T) extends Equiv[Constituent[T]] { self: Constituent[T] =>
//  def equiv(x: Constituent[T]): Boolean = equiv(this, x)
//  def equiv(x: Constituent[T], y: Constituent[T]) = x == y
//}
//
///** A Specification class holds the following properties:
// *  (1) Order restricted
// *  (2) Annotations to consider (in XML think of attributes)
// *  (3) Constituents to consider (in XML think of child elements)
// * 
// *  A Specification class is usually used as values in a Map, mapping elements to their specification.
// * 
// * @typeparam A Type of annotations
// * @typeparam C Type of constituents */
//case class Spec[K,V,C](val ordered: Boolean, val annotations: Set[Annotation[K,V]], val constituents: Set[Constituent[C]])
//
///** An EquivalenceSystem 
// * 
// *  Why contravariant in type parameter T?
// * 
// *  Why does EquivalenceSystem not extend Equiv? EquivalenceSystem is contravariant in type parameter T and Equiv is not.
// */
//trait EquivalenceSystem[-T, K, V, C] {
//  //val spec: Map[T, Spec[A,C]] // doesn't compile (and I still don't understand why :()
//  def spec(l: T): Spec[K,V,C]
//  
//  def ordered(l: T): Boolean = spec(l).ordered
//  
//  def equiv[S <: T <% Identifiable[String]](l: S, r: S): Boolean = 
//    (
//         l.id == r.id 
//      && (spec(l).annotations forall (la => spec(r).annotations exists (ra => la equiv ra)))
//      && (spec(l).constituents forall (lc => spec(r).constituents exists (rc => lc equiv rc)))
//    )
//}
//
//import scala.xml.Node
//
//object Main {
//  def main(args: Array[String]): Unit = {
//    implicit def toIdentifiable(x: Node) = new Identifiable[String] { val id = x.label }
//    implicit def toConstituent(x: String) = Constituent[String](x)
//    val system = new EquivalenceSystem[Node, String, String, String] {      
//      def spec(l: Node) = l match {
//        case Node("a", _, _@_*) => Spec(true, Set.empty, Set("b", "c"))
//      }      
//    }    
//    val l = (<a><b/><c/></a>)
//    val r = (<a><c/><b/></a>)
//    
//    system.equiv(l, r)    
//  }
//}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

trait Identifiable[T] { val id: T }
trait Annotation extends Equiv[Annotation] { def equiv(x: Annotation): Boolean = equiv(this, x) }
trait Constituent extends Equiv[Constituent] { def equiv(x: Constituent): Boolean = equiv(this, x) }

case class Spec(val ordered: Boolean, val annotations: Set[Annotation], val constituents: Set[Constituent])

trait EquivalenceSystem[-T] {
  def spec(l: T): Spec
  
  def ordered(l: T): Boolean = spec(l).ordered
  
  // Regarding XML:
  // (forall l.attributes in spec(l).annotations) there exists an equivalent attribute in r.attributes
  // (forall l.child in spec(l).constituents) there exists an equivalent element in r.child
  def equiv[S <: T <% Identifiable[String]](l: S, r: S): Boolean = 
    (
         l.id == r.id 
      && (spec(l).annotations forall (la => spec(r).annotations exists (ra => la equiv ra)))   // wrong, see above
      && (spec(l).constituents forall (lc => spec(r).constituents exists (rc => lc equiv rc))) // wrong, see above
    )
}

import scala.xml.Node

object Main {
  def main(args: Array[String]): Unit = {
    implicit def toIdentifiable(x: Node) = new Identifiable[String] { val id = x.label }

    val system = new EquivalenceSystem[Node] {
      
      implicit def toAttr(x: (String, String)) = Attr(x._1, x._2)      
      case class Attr(key: String, value: String) extends Annotation { def equiv(x: Annotation, y: Annotation) = x == y }
      
      implicit def toCons(x: String) = Cons(x)
      implicit def toCons(x: (String, String)) = Cons(x._1+":"+x._2)      
      case class Cons(qname: String) extends Constituent { def equiv(x: Constituent, y: Constituent) = x == y }
      
      def spec(l: Node) = l match {
        case Node("a", _, _@_*) => Spec(true, Set.empty, Set("b", "c"))
        case _                  => Spec(false, Set.empty, Set.empty)
      }      
    }
    
    val l = (<a><b/><c/></a>)
    val r = (<a><c/><b/></a>)
    
    system.equiv(l, r)    
  }
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//@todo modul system
//@todo scoping (no namespaces explicitly but interpretation dependent)

//case class Slit[T](val before: Option[Seq[T]],val focus: Option[T],val after: Seq[T])
//
//case class EquivalenceSpecification(val attributes: Set[String], val constituents: Seq[String], val ordered: Boolean)
//  
//trait EquivalenceSystem[-T] {
//  def specification[S <: T <% Identifiable[String]](l: S): EquivalenceSpecification
//  
//  def findEquivTo[S <: T <% Identifiable[String]](ctx: Seq[S],elem: S): Slit[S] = findEquivTo(ctx,elem,Nil)
//  
//  private def findEquivTo[S <: T <% Identifiable[String]](ctx: Seq[S],elem: S,acc: Seq[S]): Slit[S] = ctx match {
//    case Nil | Seq.empty         => Slit(None,None,acc)
//    case _ if equiv(elem,ctx(0)) =>
//      if(ordered(elem)) acc match {
//        case Nil | Seq.empty => Slit(None,Some(ctx(0)),ctx.drop(1))
//        case _               => Slit(Some(acc),Some(ctx(0)),ctx.drop(1))
//      } else Slit(None,Some(ctx(0)),acc ++ ctx.drop(1))
//    case _                       => findEquivTo(ctx.drop(1),elem,acc ++ Seq(ctx(0)))
//  }
//
//  def equiv[S <: T <% Identifiable[String]](l: S, r: S): Boolean = 
//    (   
//        l.id == r.id
//     && (specification(l).attributes forall (la => specification(r).attributes exists (la == _)))
//     && equiv(specification(l).constituents, specification(r).constituents, specification(l).ordered)
//    )
//  
//  //def equiv[S <: T <% Identifiable[String]](ls: Seq[S],rs: Seq[S]): Boolean = ls.length == rs.length && (ls forall (e => rs exists (equiv(e,_))))
//  def equiv[S <: T <% Identifiable[String]](ls: Seq[S],rs: Seq[S], ordered: Boolean): Boolean = {
//    if(ls.length != rs.length)
//      false
//    else
//      if(ordered)
//        (ls, rs) match {
//          case (Nil, Nil) | (Seq.empty, Seq.empty) => true
//          case  _ => equiv(ls(0), rs(0)) && equiv(ls.drop(1), rs.drop(1), ordered)
//        }
//      else (ls forall (l => rs exists (equiv(l,_))))
//  }
//  
//  def ordered[S <: T <% Identifiable[String]](l: S): Boolean = specification(l).ordered
//}

//object Test {
//  def main(args: Array[String]) {
//    val model = new EQS[scala.xml.Node] {
//      //import IdentifiableNode._
//      val specification = EquivalenceSystemParser.parse("etc/eq/svnbook.eq".toFile)
//    }
//    
//    ()
//  }
//}

//import scala.collection.mutable.{Map, HashMap}
//
//trait System {
//  
//  type T <: Item
//  abstract class Item { 
//    def equivTo(that: T): Boolean
//  }
//  
//  def verify(l: T, r: T): Boolean = l equivTo r
//
//}

//trait EqRel[T] extends (T, T) => 

//trait System[-T] {
//  
//  type F <: (T, T) => Boolean
//
//  private val map: Map[String, F] = Map()
// 
//}
